開啟頁面自動登入並回來-攔截器(升級版)
之前寫過一篇文章,講解如何 ofollow,noindex">解耦頁面跳轉和自動登入 ,至於思想想必看過那篇文章的人都懂了,簡單說就是很多地方都會跳轉某個頁面(稱呼為A頁面),但A頁面需要登入後才能載入資料,我們不希望發起跳轉的頁面負責登入判斷和登入的工作,即:登入判斷和跳轉登入應該是A頁面自己的份內工作。

inteceptor.png
只是,之前的實現有程式碼侵入性,需要一個讓A頁面繼承一個叫 InterceptorActivity
的頁面(負責註解解析和校驗),又恐怕大家專案都已經有了一個所謂的BaseActivity,逼不得已要改動此BaseActivity讓其繼承 InterceptorActivity
, 很明顯這是有侵入性的。
新的實現方案是建立一個無UI的Fragment的代理,它負責做真正的 startActivityForResult()
,隨後的activityResult通過它們之間的callback傳給建立無UI Fragment的Activity或者Fragment,這原理其實很簡單。因此,我們又有了另外一個發現:簡化 startActivityForResult()
, 即:以掛回調的方式處理activityResult。
其實,Android 6.0許可權請求的API跟 startActivityForResult()
及其類似( requestPermissions (String[] permissions, int requestCode)
和 onRequestPermissionsResult (int requestCode, String permissions[], int[] grantResults)
),在此之前也寫過篇文章如何通過此原理簡化了 Android 6.0許可權的請求 。
如下演示瞭如何在Activity、Fragment中開啟需要先登入的訂單詳情頁,以及不僅要登入還要授權的管理員頁面,還有在Activity和Fragment中以掛回調的方式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(this) { /** * 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
物件的建立可以在Activity也可在Fragment。
如果需要多個校驗攔截,配置方式跟以前一樣(當所有Interceptor都校驗通過才會觸發 invoke()
執行):
@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(this) { @Override public void invoke() { TextView textView = findViewById(R.id.contentView); textView.setText("This The Admin Manager page"); } }); } }
2. 以掛回調的方式接收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) { if (resultCode == Activity.RESULT_OK) { String phoneNum = Util.resolvePhoneNumber(getApplicationContext(), data.getData()); Toast.makeText(MainActivity.this, "phone number: " + phoneNum, Toast.LENGTH_SHORT).show(); } } });
3. 如何實現之ActivityResult:
public class ActivityResult { private static final String TAG = ActivityResult.class.getSimpleName(); private List<Interceptor> mInterceptors = new ArrayList<>(); private Lazy<ResultFragment> mResultFragment; public ActivityResult(FragmentActivity activity) { mResultFragment = getLazySingleton(activity.getSupportFragmentManager()); findInterceptors(activity); } public ActivityResult(Fragment fragment) { mResultFragment = getLazySingleton(fragment.getChildFragmentManager()); 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 callback) { mResultFragment.get().intercept(new OnResultCallback() { @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { for (Interceptor interceptor : mInterceptors) { if (interceptor.getRequestCode() == requestCode) { if (resultCode == Activity.RESULT_OK) { verifyInterceptors(callback); break; } else if (resultCode == Activity.RESULT_CANCELED) { callback.finishSelf(); break; } } } } }); // verify interceptors if (!mInterceptors.isEmpty()) { verifyInterceptors(callback); } } 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(); } } } } private void verifyInterceptors(OnInterceptResult callback) { if (mInterceptors.isEmpty()) { return; } for (int i = 0; i < mInterceptors.size(); i++) { Interceptor interceptor = mInterceptors.get(i); if (interceptor.isValid(mResultFragment.get().getContext())) { if (i == mInterceptors.size() - 1) { callback.invoke(); break; } } else { interceptor.process(mResultFragment.get()); break; } } } @NonNull private Lazy<ResultFragment> getLazySingleton(@NonNull final FragmentManager fragmentManager) { return new Lazy<ResultFragment>() { private ResultFragment permissionsFragment; @Override public synchronized ResultFragment get() { if (permissionsFragment == null) { permissionsFragment = getPermissionsFragment(fragmentManager); permissionsFragment.setLogging(true); } return permissionsFragment; } }; } private ResultFragment getPermissionsFragment(@NonNull final FragmentManager fragmentManager) { ResultFragment permissionsFragment = (ResultFragment) fragmentManager.findFragmentByTag(TAG); boolean isNewInstance = permissionsFragment == null; if (isNewInstance) { permissionsFragment = new ResultFragment(); fragmentManager .beginTransaction() .add(permissionsFragment, TAG) .commitNow(); } return permissionsFragment; } @FunctionalInterface interface Lazy<V> { V get(); } }
兩個建構函式使得可以在Activity和Fragment裡工作,瞭解之前文章的朋友會發現解析註解的工作現在放在了 ActivityResult
裡了,不再依賴Override Activity了。 ActivityResult
的作用就是建立 ResultFragment
並委託其 startActivityForResult()
, 隨後的結果返回通過 OnResultCallback
或者 OnInterceptResult
傳遞出去。
4. 如何實現之ResultFragment:
/** * It's a interceptor fragment and it is used to startActivityForResult() * and pass activity result to its observer via callback. */ public class ResultFragment extends Fragment { private static final String TAG = "PermissionsFragment"; public static final int REQUEST_CODE = 9998; private OnResultCallback mOnResultCallback; private boolean mLogging; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setRetainInstance(true); } void startActivityForResult(Intent intent, OnResultCallback callback) { startActivityForResult(intent, REQUEST_CODE); mOnResultCallback = callback; } void intercept(OnResultCallback callback) { mOnResultCallback = callback; } @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); mOnResultCallback.onActivityResult(requestCode, resultCode, data); } }
ResultFragment的實現非常簡單,它就是一個普通Fragment,負責把activityResult通過callback回傳給它的建立者。
完整實現可以參考 這裡