關於原生權限管理AppOps
AppOps雖然涵蓋了App的權限管理,但是Google原生的設計並不僅僅是對“權限”的管理,而是對App的“動作”的管理。我們平時講的權限管理多是針對具體的權限(App開發者在Manifest裏申請的權限),而AppOps所管理的是所有可能涉及用戶隱私和安全的操作,包括access notification, keep weak lock, activate vpn, display toast等等,有些操作是不需要Manifest裏申請權限的。
獲取到當前應用棧頂的Activity
使用Application中的ActivityLifecycleCallbacks,使用它的 registerActivityLifecycleCallbacks,感知Activity聲明週期變化,獲取到當前應用棧頂的Activity,這樣我們就不需要自己手動傳入了。
public class PermissionActivityLifecycle implements Application.ActivityLifecycleCallbacks {
WeakReference<Activity> topActWeakReference;
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
//原則上只需要onResume,兼容如果在onCreate的時候做權限申請保證此時有Activity對象
topActWeakReference = new WeakReference<>(activity);
}
//.....
@Override
public void onActivityResumed(Activity activity) {
topActWeakReference = new WeakReference<>(activity);
}
//.....
}
複製代碼
註冊它僅僅需要一個Application:
/**
* @param context Application
*/
private void registerLifecycle(Application context) {
if (null != lifecycle) {
context.unregisterActivityLifecycleCallbacks(lifecycle);
}
lifecycle = new PermissionActivityLifecycle();
context.registerActivityLifecycleCallbacks(lifecycle);
}
複製代碼
這樣一來,只要調用了初始化方法registerLifecycle,我們就能提供提供棧頂Activity了
/**
* 獲取棧頂Activity
*
* @return 當前應用棧頂Activity
* @throws InitException 初始化失敗
* @throws ContainerStatusException Activity狀態異常
*/
private Activity getContainer() {
// may auto init failed
if (null == lifecycle || null == lifecycle.topActWeakReference) {
throw new InitException();
}
// activity status error
if (null == lifecycle.topActWeakReference.get() || lifecycle.topActWeakReference.get().isFinishing()) {
throw new ContainerStatusException();
}
return lifecycle.topActWeakReference.get();
}
複製代碼
脫離Activity和Fragment,也無需重寫onPermissionResult了,只需要一個ApplicationContext初始化即可。
避免application中初始化,利用ContentProvider初始化
參考Lifecycle組件的初始化:
//lifeCycle定義的初始化Provider
public class LifecycleRuntimeTrojanProvider extends ContentProvider {
@Override
public boolean onCreate() {
LifecycleDispatcher.init(getContext());
ProcessLifecycleOwner.init(getContext());
return true;
}
}
複製代碼
和它的Manifest文件:
<application>
<provider
android:name="android.arch.lifecycle.LifecycleRuntimeTrojanProvider"
android:authorities="${applicationId}.lifecycle-trojan"
android:exported="false"
android:multiprocess="true" />
</application>
複製代碼
參照它的實現給我們提供了一個很好的思路,我們可以自定義Provider去初始化一些庫或者其他的內容,現在我們寫一個自己的initContentProvider:
public class InitProvider extends ContentProvider {
@Override
public boolean onCreate() {
//初始化我們的庫
SoulPermission.getInstance().autoInit((Application) getContext());
return true;
}
//......
}
複製代碼
在庫的AndroidManifest文件中聲明:
<application>
<provider android:authorities="${applicationId}.permission.provider"
android:name=".permission.InitProvider"
android:multiprocess="true"
android:exported="false"/>
</application>
複製代碼
至於爲什麼這個Context就是Application,我們可以參考ActivityThread中的對ContentProvider的初始化:
public void handleInstallProvider(ProviderInfo info) {
//即我們的應用的Application
installContentProviders(mInitialApplication, Arrays.asList(info));
}
複製代碼
至此,我們權限申請流程就跟Activity、Fragment、乃至Context都沒有關係了
https://github.com/soulqw/SoulPermission