打開頁面自動登錄并回來-攔截器(升級版)

??之前寫過一篇文章茧痕,講解如何解耦頁面跳轉(zhuǎn)和自動登錄,至于思想想必看過那篇文章的人都懂了,簡單說就是很多地方都會跳轉(zhuǎn)某個頁面(稱呼為A頁面)龟虎,但A頁面需要登錄后才能加載數(shù)據(jù),我們不希望發(fā)起跳轉(zhuǎn)的頁面負責(zé)登錄判斷和登錄的工作茎用,即:登錄判斷和跳轉(zhuǎn)登錄應(yīng)該是A頁面自己的份內(nèi)工作遣总。

inteceptor.png

??只是,之前的實現(xiàn)有代碼侵入性轨功,需要改動現(xiàn)有的BaseActivity, 讓Activity賦予能做注解解析功能旭斥,很明顯這是有侵入性的。
??新的實現(xiàn)方案是創(chuàng)建一個無UI的Fragment的代理古涧,它負責(zé)做真正的startActivityForResult()垂券,隨后的activityResult通過它們之間的callback傳給創(chuàng)建無UI Fragment的Activity或者Fragment,這原理其實很簡單羡滑。因此菇爪,我們又有了另外一個發(fā)現(xiàn):簡化startActivityForResult(), 即:以掛回調(diào)的方式處理activityResult。
??其實柒昏,Android 6.0權(quán)限請求的API跟startActivityForResult()及其類似(requestPermissions (String[] permissions, int requestCode)onRequestPermissionsResult (int requestCode, String permissions[], int[] grantResults))凳宙,在此之前也寫過篇文章如何通過此原理簡化了Android 6.0權(quán)限的請求

如下演示了幾個案例:

  1. 如何在Activity中打開需要先登錄的訂單詳情頁职祷;
  2. 如何在Fragment中打開需要先登錄的訂單詳情頁氏涩;
  3. 打開不僅要登錄還要授權(quán)的管理員頁面;
  4. 在Activity中以掛回調(diào)的方式startActivityForResult()接收activityResult有梆;
  5. 在Fragment中以掛回調(diào)的方式startActivityForResult()接收activityResult是尖;
activity_result.gif

1. Activity攔截器新的接入方式:

// 1. open OrderDetailActivity
Intent intent = new Intent(context, OrderDetailActivity.class);
startActivity(intent);

// 2. implementation of OrderDetailActivity
@InterceptWith(LoginInterceptor.class)
public class OrderDetailActivity extends AppCompatActivity {

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

        ActivityResult result = new ActivityResult(this);
        result.intercept(new OnInterceptResult() {
           
            /**
             * init data or load data from http and so on after all interceptors is validated.
             */
            @Override
            public void invoke() {
                TextView imageView = findViewById(R.id.contentView);
                imageView.setText("This Is the Order Detail Page");
            }
        });
    }
}

ActivityResult對象的創(chuàng)建可以在Activity也可在Fragment。
如果需要多個校驗攔截泥耀,配置方式跟以前一樣(當(dāng)所有Interceptor都校驗通過才會觸發(fā)invoke()執(zhí)行):

@InterceptWith({LoginInterceptor.class, PermissionInterceptor.class})
public class AdminActivity extends AppCompatActivity {

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

        ActivityResult activityResult = new ActivityResult(this);
        activityResult.intercept(new OnInterceptResult() {
            @Override
            public void invoke() {
                TextView textView = findViewById(R.id.contentView);
                textView.setText("This The Admin Manager page");
            }
        });
    }
}

那么LoginInterceptor.java是一個根據(jù)業(yè)務(wù)定義的校驗器饺汹,讓我們看看如何實現(xiàn):

public class LoginInterceptor extends Interceptor {

    @Override
    public boolean isValid(Context context) {
        return LoginActivity.isLogin(context);
    }

    @Override
    public Intent getTargetIntent(Context context) {
        return new Intent(context, LoginActivity.class);
    }
}

其實,只要給出校驗規(guī)則以及校驗不通過要跳轉(zhuǎn)的頁面Intent即可痰催。

2. 以掛回調(diào)的方式接收activityResult:

Intent intent = new Intent(Intent.ACTION_PICK, ContactsContract.CommonDataKinds.Phone.CONTENT_URI);
ActivityResult activityResult = new ActivityResult(this);
activityResult.startActivityForResult(intent, new OnResultCallback() {

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        String phoneNum = Util.resolvePhoneNumber(getApplicationContext(), data.getData());
        Toast.makeText(MainActivity.this, "phone number: " + phoneNum, Toast.LENGTH_SHORT).show();
    }
});

3. 如何實現(xiàn)之ActivityResult:

public class ActivityResult {
    private static final String TAG = ActivityResult.class.getSimpleName();

    private FragmentManager mFragmentManager;
    private List<Interceptor> mInterceptors = new ArrayList<>();
    private Lazy<ResultFragment> mResultFragment;
    private FragmentActivity mFragmentActivity;

    public ActivityResult(FragmentActivity activity) {
        mFragmentActivity = activity;
        mFragmentManager = activity.getSupportFragmentManager();
        mResultFragment = getLazySingleton();
        findInterceptors(activity);
    }

    public ActivityResult(Fragment fragment) {
        mFragmentActivity = fragment.getActivity();
        mFragmentManager = fragment.getChildFragmentManager();
        mResultFragment = getLazySingleton();
        findInterceptors(fragment);
    }

    /**
     * Convenient method to start activity for result.
     */
    public void startActivityForResult(Intent intent, OnResultCallback callback) {
        mResultFragment.get().startActivityForResult(intent, callback);
    }

    /**
     * Check if interceptors specified with annotation {@link InterceptWith} are valid or not.
     */
    public void intercept(final OnInterceptResult onInterceptResult) {
        boolean isNewInstance = mFragmentManager.findFragmentByTag(TAG) == null;

        OnResultCallback onResultCallback = new OnResultCallback() {
            @Override
            public void onActivityResult(int requestCode, int resultCode, Intent data) {
                for (Interceptor interceptor : mInterceptors) {
                    if (requestCode == interceptor.getRequestCode()){
                        if (resultCode == Activity.RESULT_OK) {
                            verifyInterceptors(true, onInterceptResult, this);
                            break;
                        } else if (resultCode == Activity.RESULT_CANCELED) {
                            mFragmentActivity.finish();
                            break;
                        }
                    }
                }
            }
        };

        mResultFragment.get().setResultCallback(onResultCallback);

        // verify interceptors
        if (!mInterceptors.isEmpty()) {
            verifyInterceptors(isNewInstance, onInterceptResult, onResultCallback);
        }
    }

    private void findInterceptors(Object object) {
        mInterceptors.clear();
        InterceptWith annotation = object.getClass().getAnnotation(InterceptWith.class);
        if (annotation != null) {
            Class<? extends Interceptor>[] classes = annotation.value();
            for (Class<? extends Interceptor> clazz : classes) {
                try {
                    mInterceptors.add(clazz.newInstance());
                } catch (InstantiationException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * new instance need to invoke process
     */
    private void verifyInterceptors(boolean isNewInstance, OnInterceptResult onInterceptResult, OnResultCallback onResultCallback) {
        if (mInterceptors.isEmpty()) {
            return;
        }

        for (int i = 0; i < mInterceptors.size(); i++) {
            Interceptor interceptor = mInterceptors.get(i);
            if (interceptor.isValid(mResultFragment.get().getContext())) {
                // invoke callback if all validations pass
                if (i == mInterceptors.size() - 1) {
                    onInterceptResult.invoke();
                    break;
                }
            } else if (isNewInstance){
                Intent intent = interceptor.getTargetIntent(mFragmentActivity);
                int requestCode = mResultFragment.get().startActivityForResult(intent, onResultCallback);
                interceptor.setRequestCode(requestCode);
                break;
            }
        }
    }

    @NonNull
    private Lazy<ResultFragment> getLazySingleton() {
        return new Lazy<ResultFragment>() {
            private ResultFragment permissionsFragment;

            @Override
            public synchronized ResultFragment get() {
                if (permissionsFragment == null) {
                    permissionsFragment = getResultFragment();
                    permissionsFragment.setLogging(true);
                }
                return permissionsFragment;
            }
        };
    }

    private ResultFragment getResultFragment() {
        ResultFragment permissionsFragment = (ResultFragment) mFragmentManager.findFragmentByTag(TAG);
        boolean isNewInstance = permissionsFragment == null;
        if (isNewInstance) {
            permissionsFragment = new ResultFragment();
            mFragmentManager
                    .beginTransaction()
                    .add(permissionsFragment,TAG)
                    .commitNowAllowingStateLoss();
        }
        return permissionsFragment;
    }

    @FunctionalInterface
    interface Lazy<V> {
        V get();
    }
}

兩個構(gòu)造函數(shù)使得可以在Activity和Fragment里工作兜辞,了解之前文章的朋友會發(fā)現(xiàn)解析注解的工作現(xiàn)在放在了ActivityResult里了,不再需要篡改Activity了陨囊。ActivityResult的作用就是創(chuàng)建ResultFragment并委托其startActivityForResult(), 隨后的結(jié)果返回通過OnResultCallback或者OnInterceptResult傳遞出去弦疮。

4. 如何實現(xiàn)之ResultFragment:

public class ResultFragment extends Fragment {
    private static final String TAG = "ResultFragment";

    private SparseArray<OnResultCallback> mResultCallbackStorage = new SparseArray<>();
    private int mRequestCode = 200;
    private boolean mLogging;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setRetainInstance(true);
    }

    int startActivityForResult(Intent intent, OnResultCallback callback) {
        mResultCallbackStorage.put(++mRequestCode, callback);
        startActivityForResult(intent, mRequestCode);
        return mRequestCode;
    }

    void setResultCallback(OnResultCallback callback) {
        mResultCallbackStorage.put(++mRequestCode, callback);
    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        OnResultCallback callback = mResultCallbackStorage.get(requestCode);
        if (callback != null) {
            callback.onActivityResult(requestCode, resultCode, data);
        }
    }

    void setLogging(boolean logging) {
        mLogging = logging;
    }

    void log(String message) {
        if (mLogging) {
            Log.d(TAG, message);
        }
    }
}

ResultFragment的實現(xiàn)非常簡單,它就是一個普通Fragment蜘醋,負責(zé)把activityResult通過callback回傳給它的創(chuàng)建者胁塞。

完整實現(xiàn)可以參考這里

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子啸罢,更是在濱河造成了極大的恐慌编检,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件扰才,死亡現(xiàn)場離奇詭異允懂,居然都是意外死亡,警方通過查閱死者的電腦和手機衩匣,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進店門蕾总,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人琅捏,你說我怎么就攤上這事生百。” “怎么了柄延?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵蚀浆,是天一觀的道長。 經(jīng)常有香客問我搜吧,道長市俊,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任滤奈,我火速辦了婚禮摆昧,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘蜒程。我一直安慰自己据忘,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布搞糕。 她就那樣靜靜地躺著,像睡著了一般曼追。 火紅的嫁衣襯著肌膚如雪窍仰。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天礼殊,我揣著相機與錄音驹吮,去河邊找鬼。 笑死晶伦,一個胖子當(dāng)著我的面吹牛碟狞,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播婚陪,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼族沃,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起脆淹,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤常空,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后盖溺,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體漓糙,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年烘嘱,在試婚紗的時候發(fā)現(xiàn)自己被綠了昆禽。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡蝇庭,死狀恐怖醉鳖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情遗契,我是刑警寧澤辐棒,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站牍蜂,受9級特大地震影響漾根,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜鲫竞,卻給世界環(huán)境...
    茶點故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一辐怕、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧从绘,春花似錦寄疏、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至批什,卻和暖如春农曲,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背驻债。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工乳规, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人合呐。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓暮的,卻偏偏與公主長得像,于是被迫代替她去往敵國和親淌实。 傳聞我的和親對象是個殘疾皇子冻辩,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,979評論 2 355