1. 程式人生 > >Android實現點選通知欄後,先啟動應用再開啟目標Activity

Android實現點選通知欄後,先啟動應用再開啟目標Activity

情況簡述

在開發Android app的過程中,遇到這樣一個需求:app中啟動一個Service,該Service在獨立程序中執行,與伺服器保持長連線,將伺服器推送過來的訊息在通知欄中顯示,並設定點選動作,點選後跳轉到app中對應的Activity。目前遇到的問題是Service以獨立程序執行,在收到訊息並彈出通知後,app本身的程序有兩種情況:

  1. app正在執行
  2. app已退出

對於第一種情況,處理就非常簡單了,直接將引數傳入Intent並開啟對應的Activity即可。

但第二種情況比較複雜,因為app已經退出,而要開啟的Activity中的某些操作是需要依賴app的初始化的,這些初始化操作是在app啟動過程中進行的。舉個例子,一個購物應用推送了某個新商品的訊息,使用者點選通知後進入商品詳情的Activity,而該Activity中有個訂購Button,點選該Button後就會從本地中獲取使用者的Id等資訊併發一條訊息給伺服器,告訴伺服器某使用者訂購了該商品。這些使用者資訊是在app啟動時與伺服器進行一系列互動後取得的。如果app退出後直接進入詳情Activity並點選購買,就會因為獲取不到使用者資訊而出錯。

所以目前要解決的問題時,在Notification中設定點選動作,如果app本身正在執行,直接跳轉到目標Activity;如果app已經退出,先啟動app完成初始化,再跳轉到目標Activity。

方案和思路

我們假設目前有三個Activity:

  1. SplashActivity 用於顯示app大圖,同時進行使用者登入等操作,伺服器返回資料後跳轉到MainActivity。
  2. MainActivity app的主Activity。
  3. DetailActivity MainActivity中點選Button進入的Activity,用於顯示某件商品詳情。

而彈出通知的Service在另外一個程序中。

我們要達到的目的是:

  1. 點選通知欄通知,假如app正在執行,則直接跳轉到DetailActivity顯示具體內容,在DetailActivity中按Back鍵返回MainActivity
  2. 點選通知欄通知,假如app已經退出,先從SplashActivity進入,顯示app啟動介面,初始化操作完成後進入MainActivity再跳轉到DetailActivity顯示具體內容,在DetailActivity中按Back鍵返回MainActivity。

初步的思路是先判斷app程序是否存在,如果存在的話,就利用startActivities啟動MainActivity和DetailActivity。為什麼還要啟動MainActivity而不直接只啟動DetailActivity?因為有如下情況,程序中的所有Activity都已經退出了,但程序還沒有被系統回收,這時判斷程序是否存在返回true,然後只啟動DetailActivity的話,按Back鍵任務棧就直接到底,返回桌面了。而我們要的效果是按Back鍵返回上一級Activity,也就是MainActivity。

如果app程序已經退出,不存在了,此時就用一個Intent啟動應用,該Intent中包含一個Bundle, Bundle中存有啟動DetailActivity所需的引數,這個Intent傳入SplashActivity後,再由SplashActivity傳給MainActivity,在MainActivity中加入判斷,如果有該引數,則表示應用是從通知欄啟動的,要進行跳轉到DetailActivity的操作,否則就是常規啟動。

程式碼實現

有了大概的實現思路後,大家來個demo實際操作一下。
首先,我們的demo有簡單的元件:

  1. PushService,在新程序中啟動的Service,負責監聽伺服器,收到伺服器的資訊後將訊息廣播出去,在本demo中,為了簡化,只是簡單的廣播一個訊息
  2. ShowNotificationReceiver,在新程序中註冊的BroadcastReceiver,收到PushService發的訊息後,會在通知欄彈出通知
  3. NotificationReceiver, 在新程序中註冊的BroadcastReceiver,用來設定點選通知欄通知的動作,開啟app中的某個Activity
  4. SplashActivity, app啟動頁面,先是啟動圖片,3s後進入MainActivity
  5. MainActivity,app的主Activity
  6. DetailActivity,app中顯示詳情的Activity

PushService.java

首先是PushService,要在新程序中啟動,要在AndroidManifest.xml中加入以下注冊Service的程式碼

<service android:name=".PushService"
                 android:process=":push"/>
PushService的工作很簡單,啟動後發一個廣播在通知欄顯示通知,然後常駐在後臺
public class PushService extends Service{
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Log.i("PushService", "PushService onCreate");
        //用AlarmManager定時傳送廣播
        AlarmManager am = (AlarmManager)getSystemService(Context.ALARM_SERVICE);

        Intent intent = new Intent(this, ShowNotificationReceiver.class);

        PendingIntent pendingIntent =
                PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);

        am.set(AlarmManager.ELAPSED_REALTIME, SystemClock.currentThreadTimeMillis(), pendingIntent);

    }

}

ShowNotificationReceiver.java

這個廣播類用來在通知欄彈出通知

public class ShowNotificationReceiver extends BroadcastReceiver{
    private static final String TAG = "RepeatReceiver";
    @Override
    public void onReceive(Context context, Intent intent) {
        Log.d(TAG, "ShowNotificationReceiver onReceive");
        //設定點選通知欄的動作為啟動另外一個廣播
        Intent broadcastIntent = new Intent(context, NotificationReceiver.class);
        PendingIntent pendingIntent = PendingIntent.
                getBroadcast(context, 0, broadcastIntent, PendingIntent.FLAG_UPDATE_CURRENT);

        NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
        builder.setContentTitle("這就是通知的頭")
                .setTicker("這是通知的ticker")
                .setContentIntent(pendingIntent)
                .setSmallIcon(android.R.drawable.ic_lock_idle_charging);

        Log.i("repeat", "showNotification");
        NotificationManager manager = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
        manager.notify(2, builder.build());
    }
}

NotificationReceiver.java

點選通知欄後,會發送一個廣播,NotificationReceiver收到該廣播後,就會判斷,app程序是否仍然存活,根據app程序的不同狀態,定義不同的app啟動方式

public class NotificationReceiver extends BroadcastReceiver{

    @Override
    public void onReceive(Context context, Intent intent) {
        //判斷app程序是否存活
        if(SystemUtils.isAppAlive(context, "com.liangzili.notificationlaunch")){
            //如果存活的話,就直接啟動DetailActivity,但要考慮一種情況,就是app的程序雖然仍然在
            //但Task棧已經空了,比如使用者點選Back鍵退出應用,但程序還沒有被系統回收,如果直接啟動
            //DetailActivity,再按Back鍵就不會返回MainActivity了。所以在啟動
            //DetailActivity前,要先啟動MainActivity。
            Log.i("NotificationReceiver", "the app process is alive");
            Intent mainIntent = new Intent(context, MainActivity.class);
            //將MainAtivity的launchMode設定成SingleTask, 或者在下面flag中加上Intent.FLAG_CLEAR_TOP,
            //如果Task棧中有MainActivity的例項,就會把它移到棧頂,把在它之上的Activity都清理出棧,
            //如果Task棧不存在MainActivity例項,則在棧頂建立
            mainIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

            Intent detailIntent = new Intent(context, DetailActivity.class);
            detailIntent.putExtra("name", "電飯鍋");
            detailIntent.putExtra("price", "58元");
            detailIntent.putExtra("detail", "這是一個好鍋, 這是app程序存在,直接啟動Activity的");

            Intent[] intents = {mainIntent, detailIntent};

            context.startActivities(intents);
        }else {
            //如果app程序已經被殺死,先重新啟動app,將DetailActivity的啟動引數傳入Intent中,引數經過
            //SplashActivity傳入MainActivity,此時app的初始化已經完成,在MainActivity中就可以根據傳入             //引數跳轉到DetailActivity中去了
            Log.i("NotificationReceiver", "the app process is dead");
            Intent launchIntent = context.getPackageManager().
                    getLaunchIntentForPackage("com.liangzili.notificationlaunch");
            launchIntent.setFlags(
                    Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
            Bundle args = new Bundle();
            args.putString("name", "電飯鍋");
            args.putString("price", "58元");
            args.putString("detail", "這是一個好鍋, 這是app程序不存在,先啟動應用再啟動Activity的");
            launchIntent.putExtra(Constants.EXTRA_BUNDLE, args);
            context.startActivity(launchIntent);
        }
    }
}

SplashActivity.java

SplashActivity.java先是app啟動的圖片,3s後進入MainActivity, 如果啟動SplashActivity的Intent中帶有引數,就將引數取出,放入啟動MainActivity的Intent中

public class SplashActivity extends AppCompatActivity{
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_splash);
        //隱藏ActionBar
        getSupportActionBar().hide();
        //使用handler倒數3秒後進入MainActivity
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                Intent intent = new Intent(SplashActivity.this, MainActivity.class);
                //如果啟動app的Intent中帶有額外的引數,表明app是從點選通知欄的動作中啟動的
                //將引數取出,傳遞到MainActivity中
                if(getIntent().getBundleExtra(Constants.EXTRA_BUNDLE) != null){
                    intent.putExtra(Constants.EXTRA_BUNDLE,
                            getIntent().getBundleExtra(Constants.EXTRA_BUNDLE));
                }
                startActivity(intent);
                finish();
            }
        }, 3000);
    }
}

MainActivity.java

MainActivity中,如果有引數傳入,就在初始化結束後,根據引數啟動DetailActivity,如果沒有引數傳入,就此結束自己的任務

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Intent intent = new Intent(this, PushService.class);
        startService(intent);
        setTitle("MainActivity");
        Bundle bundle = getIntent().getBundleExtra(Constants.EXTRA_BUNDLE);
        if(bundle != null){
            //如果bundle存在,取出其中的引數,啟動DetailActivity
            String name = bundle.getString("name");
            String price = bundle.getString("price");
            String detail = bundle.getString("detail");
            SystemUtils.startDetailActivity(this, name, price, detail);
            Log.i(TAG, "launchParam exists, redirect to DetailActivity");
        }
    }

}

DetailActivity.java

比較簡單,顯示傳入的引數即可

public class DetailActivity extends AppCompatActivity{
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_detail);
        getSupportActionBar().setTitle("DetailActivity");
        String name = getIntent().getStringExtra("name");
        String price = getIntent().getStringExtra("price");
        String detail = getIntent().getStringExtra("detail");

        ((TextView)findViewById(R.id.name)).setText(name);
        ((TextView)findViewById(R.id.price)).setText(price);
        ((TextView)findViewById(R.id.detail)).setText(detail);
    }
}


相關推薦

Android實現通知欄啟動應用開啟目標Activity

情況簡述 在開發Android app的過程中,遇到這樣一個需求:app中啟動一個Service,該Service在獨立程序中執行,與伺服器保持長連線,將伺服器推送過來的訊息在通知欄中顯示,並設定點選動作,點選後跳轉到app中對應的Activity。目前遇到的問題是Se

Android實現通知欄通知恢復前臺

通過廣播來實現,參考 http://www.ithtw.com/5684.html(leehom2015年09月15日 於 IT十萬為什麼 發表) 我的實現方法是不通過廣播,而是通過PendingIntent實現跳轉: Intent intent = new Intent

Android 通知欄中的通知啟動應用

今天再做專案中遇到一個問題: "點選通知時, 應用不能啟動", 原始碼如下:  Context application = getApplicationContext(); Intent resultIntent = new Intent(application, Ma

Android app執行時按HOME鍵再次圖表從新呼叫啟動頁問題

如標題所述,最近被重複例項化launcher activity這個問題搞得很慘,這個問題有哪些表現呢?如下: 1. 在package installers 安裝介面安裝完一個應用後,直接開啟app,然後進入了 Activity_1, 此時再通過此activity用star

android實現空白處軟鍵盤消失事件

/** * 實現點選空白處,軟鍵盤消失事件 */ @Override public boolean dispatchTouchEvent(MotionEvent ev) { if (ev.getAction() == MotionEvent.ACTION_DOW

android 如何通知欄中的前臺服務返回當前正在進行的活動

剛開始編寫的時候在處理PendingIntent時,就是簡單的讓所開啟的intent  new出想回到的活動,但是後來發現這樣實際上只是新建了一個相同的活動覆蓋住了正在進行的活動上,就像寫的音樂播放器,在後臺的時候若我

[Android]實現擊持續錄音松開結束錄音實現隨著分貝的大小改變圖片

chang ear sage ktr activit preview enter .sh new t 顯示錄音大小的DIALOG實現 public class VioceDomio { private Context mContext; private

Android實現返回鍵返回桌面而不是退出程式

有時候我們的應用我們希望他點選一次返回鍵的時候不是退出應用而是返回到桌面,比如像QQ和微信等聊天軟體,在主介面點選返回鍵的時候不是退出應用而是返回到桌面,那麼怎麼能夠實現這種需求呢?其實也很簡單,只需要重寫onKeyDown()這個方法,在方法裡面實現返回的程式碼,這樣就可以實現這個需求了,具體程式

RecyclerView Item Item移動到 View 中間

RecyclerView Item點選後顯示到View中間,這個主要適用於視訊劇集,綜藝列表 Item 點選後顯示到View 中間,這個主要是提升使用者體驗。具體實現列入下: 方法介紹 1.判斷當前點選的 Item 是否可見 private boolean isVisi

Clipboard外掛實現複製功能並且自動跳轉

Clipboard外掛是現在流行的實現複製功能的外掛之一,公司給了一個需求,要求能實現點選複製功能,於是乎就在這給大家分享一下經驗。外掛的下載以及使用百度搜索可以搜尋到,就不多說,直接上程式碼。 html部分,Clipboard功能很強大,它可以實現文字框內的複

android 實現輸入框彈出日期選擇對話方塊(DatePickerDialog)

前言 這裡我們實現一個功能,點選日期輸入框(EditText),即可彈出日期選擇對話方塊(DatePickerDialog),等使用者選擇日期後,將使用者選擇的日期顯示在日期輸入框。 1.activity_main.xml 這裡只有一個TextView和EditText。

jquery 元素滾動條滾動至該元素位置

點選元素後,滾動條滾動至該元素位置: $('a.lead-link').bind('click', function(e) { e.preventDefault(); $('html,body').animate({ scrollTop: $(this.

zTree樹外掛實現左側樹右側展示文章列表頁面實現思路

筆者新建了一個QQ群:571278542 。歡迎大家加入! 上一篇文章中說了zTree樹外掛編寫過程。接來下,談談zTree樹外掛實現點選左側樹,右側展示文章列表頁面過程。 效果展示 2.這裡就說說思路。 channel.jsp

jquery實現其他區域清除指定div

jQuery(document).on('click', function(e) { var e = e || window.event; var idValue = jQuery(e.target).attr('id'); if (idValue != 'empty_tip') {

android實現螢幕其他地方popupwindow消失

// 解決popupWindow顯示後不消失問題 menuPopupWindow.setBackgroundDrawable(new BitmapDrawable()); menuPopupWindow

FloatingActionButton實現事件擊無響應

最近做一個App用到了FloatingActionButton,可是在實現點選事件的時候卻出了點問題。 FloatingActionButton實現點選事件之後,在onClick()函式中實現的程式碼邏輯卻沒有執行,然後就很茫然,這麼簡單的東西怎麼會出錯,然後就找了好久,最後才發現還真的是

Anaconda更新打不開閃退的修復

安裝Anaconda後,手賤點了更新,不知道更新後竟然打不開了,遂解除安裝又安裝,結果閃退後,在web頁面直接報錯,如下所示: 嘗試了各種方法,目前如下方法能夠修復: 第一步:使用管理員執行 Anaconda Prompt 第二步:輸入 conda update an

ios開發:怎麼實現一個按鈕跳轉到一個新的介面並回退到上一介面

iOS三種檢視切換的原理各不相同:UITabBarController:以平行的方式管理檢視,各個檢視之間往往關係並不大,每個加入到UITabBarController的檢視都會進行初始化即使當前不顯示在介面上,相對比較佔用記憶體。UINavigationControlle

Android 按鈕切換背景同時修改文字顏色

     其實原理很簡單,用到的是selector,用來設定android:background和android:textcolor屬性,selector可以用來設定預設時候、點選時候的背景圖片和文字

使用SwingWorker 按鈕按鈕不讓在處理完之後顯示為可

public void actionPerformed(ActionEvent e) { btn_convert.setEnabled(false); new ConvtSwingWorker().execute(); } private class Con