Android權限適配,動態申請權限

 

關於原生權限管理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


 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章